import requests
from requests.adapters import HTTPAdapter
from network.api_header_type import ApiHeaderType
from network.api_exception import ApiException
from network.api_response import ApiResponse

class ApiModel:
    '''
    Http请求封装对象，请使用单例对象
    '''

    # 单例实例
    __instance = None
    __is__first = True
    # session对象
    __session :requests.Session = None
    
    # 重写new方法，是为了完成单例模式中的对象地址唯一
    def __new__(cls, *args, **kwargs):
        # 判断是否通过这个类创建过对象
        if not cls.__instance:#如果not instance（即非None）
            # 创建对象保存起来
            cls.__instance = object.__new__(cls)#给instance赋值
        # 如果有值，直接返回
        return cls.__instance

    def __init__(self):
        # 判断是否第一次
        # 此处需要保证只赋值一次，第二次开始创建对象时不会重新赋值，一直用第一个值
        if ApiModel.__is__first:
            # 设置类属性is__first为False
            ApiModel.__is__first = False
            # 创建session对象并实例化
            self.__session = requests.Session()
		    # 在session实例上挂载adapters实例，目的就是请求异常时，自动重试
            self.__session.mount("http://",HTTPAdapter(max_retries=3))
            self.__session.mount("https://",HTTPAdapter(max_retries=3))
        else: 
            raise Exception("Cannot instantiate directly, use ApiModel.getInstance() instead")
        
    @staticmethod
    def getInstance():
        '''
        获取单例对象
        '''
        if not ApiModel.__instance:
            ApiModel.__instance = ApiModel()
        return ApiModel.__instance
    
    def get(self, url :str, params=None, headers=ApiHeaderType.json.value, timeout=10):
        '''
        GET请求封装
        param:
         - url: 请求连接
         - params: 请求参数
         - headers: 请求头部
         - timeout: 超时时间
        '''
        if not url:
            raise ApiException("ApiModel.getInstance().get url is null")
        
        resp :ApiResponse = None
        try :
            obj = self.__session.get(url, params=params, headers=headers, timeout=timeout)
            resp = ApiResponse.fromResponse(obj)
        except Exception as e :
            info = f"ApiModel.getInstance().get error\nurl: {url}"
            raise ApiException(info)
        return resp
    
    def post(self, url :str, params=None, headers=ApiHeaderType.json.value, timeout=10):
        '''
        POST请求封装
        param:
         - url: 请求连接
         - params: 请求参数
         - headers: 请求头部
         - timeout: 超时时间
        '''
        if not url:
            raise ApiException("ApiModel.getInstance().post url is null")

        resp :ApiResponse = None
        try:
            obj = self.__session.post(url, params=params, json=params, headers=headers, timeout=timeout)
            resp = ApiResponse.fromResponse(obj)
        except Exception as e:
            info = f"ApiModel.getInstance().post error\nurl: {url}"
            raise ApiException(info)
        return resp
    
    def put(self,url :str, params=None, headers=ApiHeaderType.json.value, timeout=10):
        """
		PUT请求封装
		:param url: 接口地址
		:param data: 参数放在表单中
		:param json: 参数放在请求体中，一般是json
		:return:
		"""
        if not url:
            raise ApiException("ApiModel.getInstance().put url is null")

        resp :ApiResponse = None
        try:
            obj = self.__session.post(url, params=params, json=params, headers=headers, timeout=timeout)
            resp = ApiResponse.fromResponse(obj)
        except Exception as e:
            info = f"ApiModel.getInstance().put error\nurl: {url}"
            raise ApiException(info)
        return resp
            
    def delete(self,url :str, params=None, headers=ApiHeaderType.json.value, timeout=10):
        """
		DELETE请求封装
		:param url: 接口地址
		:param params: 接口参数
		:return:
		"""
        if not url:
            raise ApiException("ApiModel.getInstance().delete url is null")
        
        resp :ApiResponse = None
        try:
            obj = self.__session.post(url, params=params, json=params, headers=headers, timeout=timeout)
            resp = ApiResponse.fromResponse(obj)
        except Exception as e:
            info = f"ApiModel.getInstance().delete error\nurl: {url}"
            raise ApiException(info)
        return resp
 
    def __del__(self):
        """
        当实例被销毁时，释放session所持有的连接
        :return:
        """
        if self.__session:
             self.__session.close()